/*
* @author Galaburda Oleg a_[w]
* http://actualwave.com/ 
*
*/

package ua.org.enginer.utils {
	import flash.net.URLVariables;
	

		/**
		* Упаковывает объекты в формат GET запроса. В отличии от стандартного метода, позволяет упаковывать массивы и объекты(ассоциативные массивы).
		*
		* <ul><li>{obj:{paramName1:"some string", paramName2:1000}} преобразует в ?obj[paramName1]=some%20string&obj[paramName2]=1000</li>
		* <li>{arr:["some string", 1000]} преобразует в ?arr[0]=some%20string&arr[1]=1000</li></ul>
		* 
		* @playerversion Flash 9.0.28.0
		* @langversion 3.0
		*/
	public class URLPacker extends Object{

		/**
		*
		* @private (constant)
		*/
		static public const SPACER:String = '&';

		/**
		*
		* @private (constant)
		*/
		static public const IDENTIFIER:String = '?';

		/**
		*
		* @private (constant)
		*/
		static public const LBRACKET:String = '[';

		/**
		*
		* @private (constant)
		*/
		static public const RBRACKET:String = ']';

		/**
		*
		* @private (constant)
		*/
		static public const EQUAL:String = '=';

		/**
		*
		* @private (constant)
		*/
		static public const TRUE:String = 'true';

		/**
		*
		* @private (constant)
		*/
		static public const FALSE:String = 'false';

		/**
		*
		* @public
		*/
		static public var useNumberInsteadOfBoolean:Boolean = false;

		/**
		* Упаковка данных в строку формата GET запроса.
		* 
		* @param o
		* @return String
		* @playerversion Flash 9.0.28.0
		* @langversion 3.0
		*/
		static public function pack(o:Object):String{
			return packObject(o, '').substr(1);
		}

		/**
		*
		* @private 
		* @param o
		* @param nm
		* @return String
		* @playerversion Flash 9.0.28.0
		* @langversion 3.0
		*/
		static private function packObject(o:Object, nm:String):String{
			var ret:String = '';
			var name:String;
			var value:*;
			for(var p:String in o){
				value = o[p];
				name = nm ? nm+LBRACKET+encodeURI(p)+RBRACKET : encodeURI(p);
				if(value is Number || value is String){
					ret += SPACER+name+EQUAL+encodeURI(String(value));
				}else if(value is Boolean){
					ret += SPACER+name+EQUAL;
					if(useNumberInsteadOfBoolean) ret += String(value ? 1 : 0);
					else ret += String(value);
				}else if(value===null) ret += SPACER+name+EQUAL;
				else if(!value) continue;
				else ret += packObject(value, name);
			}
			return ret;
		}
		
		/**
		* Упаковка данных в объект URLVariables, для отправки запроса.
		* 
		* @param input
		* @param output
		* @param encodeValue
		* @return String
		* @playerversion Flash 9.0.28.0
		* @langversion 3.0
		*/
		static public function packInto(input:Object, output:Object=null, encodeValue:Boolean=false):Object{
			if(!output) output = new URLVariables();
			packObjectInto(input, '', output, encodeValue);
			return output;
		}

		/**
		*
		* @private 
		* @param input
		* @param name
		* @param output
		* @param encodeValue
		* @return String
		* @playerversion Flash 9.0.28.0
		* @langversion 3.0
		*/
		static private function packObjectInto(input:Object, nm:String, output:Object, encodeValue:Boolean=true):void{
			var name:String;
			var value:*;
			for(var p:String in input){
				value = input[p];
				name = nm ? nm+LBRACKET+encodeURI(p)+RBRACKET : encodeURI(p);
				if(value is Number || value is String){
					output[name] = encodeValue ? encodeURI(value) : value;
				}else if(value is Boolean){
					if(useNumberInsteadOfBoolean) output[name] = String(value ? 1 : 0);
					else output[name] = String(value);
				}else if(value===null) output[name] = null;
				else if(!value) continue;
				else packObjectInto(value, name, output, encodeValue);
			}
		}

		/**
		* Распаковка данных из строки формата GET запроса.
		* @public
		* @param str
		* @return Object
		* @playerversion Flash 9.0.28.0
		* @langversion 3.0
		*/
		static public function unpack(str:String, output:Object=null):Object{
			if(str.charAt(0)==IDENTIFIER) str = str.substr(1);
			if(!output) output = {};
			var arr:Array = str.split(SPACER);
			var len:int = arr.length;
			for(var i:int=0; i<len; i++) unpackVariable(arr[i], output);
			return output;
		}
		
		
		/**
		* 
		* @public
		* @param input
		* @param output
		* @param decodeValue
		* @return Object
		* @playerversion Flash 9.0.28.0
		* @langversion 3.0
		*/
		static public function unpackFrom(input:Object, output:Object=null, decodeValue:Boolean=false):Object{
			if(!output) output = {};
			for(var p:String in input) addVariable(p, input[p], output, decodeValue);
			return output;
		}

		/**
		*
		* @private
		* @param str
		* @param target
		* @param decodeValue
		* @return void
		* @playerversion Flash 9.0.28.0
		* @langversion 3.0
		*/
		static private function unpackVariable(str:String, target:Object, decodeValue:Boolean=true):void{
			var equi:int = str.indexOf(EQUAL);
			if(equi<0) equi = str.length;
			addVariable(str.substr(0, equi), str.substr(equi+1), target, decodeValue);
		}
		/**
		 * 
		 * @private (constant)
		 */
		static private const BRACERS_REGEXP:RegExp = /[^\[\]]+/g;
		/**
		 * 
		 * @private
		 * @param name
		 * @param value
		 * @param target
		 * @param decodeValue
		 * @return void
		 * @playerversion Flash 9.0.28.0
		 * @langversion 3.0
		 */
		static private function addVariable(name:String, value:String, target:Object, decodeValue:Boolean=true):void{
			BRACERS_REGEXP.lastIndex = 0;
			var arr:Array = name.match(BRACERS_REGEXP);
			var name:String;
			var len:int = arr.length-1;
			for(var i:int=0; i<len; i++){
				name = decodeURI(arr[i]);
				if(!target[name]) target[name] = isNaN(Number(arr[i+1])) ? {} : [];
				target = target[name];
			}
			name = decodeURI(arr[len]);
			var newValue:* = getVariableValue(value, decodeValue);
			if(name in target){
				var savedValue:* = target[name];
				if(savedValue is Array){
					savedValue.push(newValue);
				}else target[name] = [savedValue, newValue];
			}else target[name] = newValue;
		}
		/**
		 * 
		 * @private
		 * @param value
		 * @param decodeValue
		 * @return void
		 * @playerversion Flash 9.0.28.0
		 * @langversion 3.0
		 */
		static private function getVariableValue(value:String, decodeValue:Boolean=true):*{
			var newValue:*;
			if(value){
				if(isNaN(Number(value))){
					if(useNumberInsteadOfBoolean) newValue = decodeValue ? decodeURI(value) : value;
					else{
						if(value==TRUE) newValue = true;
						else if(value==FALSE) newValue = false;
						else newValue = newValue = decodeValue ? decodeURI(value) : value;
					}
				}else newValue = Number(decodeValue ? decodeURI(value) : value);
			}else newValue = null;
			return newValue;
		}
	}
}